﻿package org.kolonitsky.alexey.net
{
    import com.adobe.images.JPGEncoder;
    
    import flash.display.BitmapData;
    import flash.utils.ByteArray;
    import flash.utils.Endian;
    
    /**
     *  Encode object to valid multipart post data. Encoding by convert any 
     *  object field to equalent post paramtenter. UploaderPostHelper define 4 
     *  type of data:
     *  
     *  <ul>
     *    <li>String</li>
     *    <li>Number</li>
     *    <li>BitmapData</li>
     *    <li>ByteArray</li>
     *  </ul>
     *  
     *  @example The following code pass object as paramter to post data.
     *  
     *  <listing version="3.0">
     *  
     *  var uploadPostData:UploadPostHelper = new UploadPostHelper();
     *  var urlRequest:URLRequest = new URLRequest();
     *  urlRequest.url = "http://example.com/uploader.php"
     *  urlRequest.data = uploadPostData.encode(slots);
     *  urlRequest.method = URLRequestMethod.POST;
     *  urlRequest.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + uploadPostData.boundary));
     *  urlRequest.requestHeaders.push(new URLRequestHeader('Cache-Control', 'no-cache'));
     *  
     *  </listing>
     *  
     *  @author Alexey Kolonitsky
     *  @version 2009.03.23
     */
    public class UploadPostHelper 
    {
        /** 
         *  Linebreak code, two byte 
         */
        public static const LINEBREAK:int = 0x0D0A;   //2 byte
        
        /** 
         *  Boundary used to break up different parts of the http POST body. 
         *  This parameter need to 
         */
        public var boundary:String;
        
        /**
         *  @constructor
         */ 
        public function UploadPostHelper()
        {
            data = new ByteArray();
            
            boundary = "";
            for (var i:int = 0; i < 0x20; i++ ) 
                boundary += String.fromCharCode( int( 97 + Math.random() * 25 ) );
        }
        
        /**
         *
         */ 
        public function encode(vars:Object):ByteArray
        {
            data.endian = Endian.BIG_ENDIAN;
            
            for (var key:String in vars)
                addParam(key, vars[key]);
            
            addParam('Upload', 'Submit Query');
            
            // Cloase tag
            writeString('--' + boundary + '--');
            
            return data;
        }
        
        
        //----------------------------------------------------------------------
        //  
        //  Private logic
        //  
        //----------------------------------------------------------------------
        
        private var data:ByteArray;
        
        /**
         * Create post data to send in a UrlRequest
         */
        private function addParam(key:String, value:*):void 
        {
            if (value is String)
            {
                addStringParam(key, value as String);
            }
            else if (value is Number || value is int || value is uint)
            {
                addStringParam(key, String(value));
            }
            else if (value is BitmapData)
            {
                addImageParam(key, value as BitmapData);
            }
            else if (value is Array)
            {
                addStringParam(key, String(value));
            }
            else if (value is Object)
            {
                addSendFileParam(key, value);
            } 
            else
            {
                trace("WAR: UploadPostHelper. Unknown POST " +
                    "parameter type.");
            }
        }
        
        
        private function addStringParam(key:String, value:String):void
        {
            writeLine('--' + boundary);
            writeLine('Content-Disposition: form-data; name="' + key + '"');
            
            data.writeShort(LINEBREAK);
            data.writeUTFBytes(value);
            data.writeShort(LINEBREAK);
        }
        
        private function addImageParam(key:String, value:BitmapData):void
        {
            var imageBytes: ByteArray = new JPGEncoder(80).encode(value)
            
            writeLine("--" + boundary);
            writeLine('Content-Disposition: form-data; name="' + key + '"; filename="' + key + '.jpeg"');
            writeLine('Content-Type: image/jpeg');
            
            data.writeShort(LINEBREAK);
            data.writeBytes(imageBytes, 0, imageBytes.length);
            data.writeShort(LINEBREAK);
        }
        
        private function addSendFileParam(key:String, value:Object):void
        {
            writeLine("--" + boundary);
            writeLine('Content-Disposition: form-data; name="' + key + '"; filename="' + value.name + '"');
            writeLine('Content-Type: audio/mpeg');
            
            var ba: ByteArray = value.data as ByteArray;
            data.writeShort(LINEBREAK);
            data.writeBytes(ba, 0, ba.length);
            data.writeShort(LINEBREAK);
        }
        
        private function writeString(str:String):void
        {
            var l: int = str.length;
            for (var i:int=0; i < l; i++)
                data.writeByte(str.charCodeAt(i));
        }
        
        private function writeLine(str:String):void
        {
            var l: int = str.length;
            for (var i:int=0; i < l; i++)
                data.writeByte(str.charCodeAt(i));
            data.writeShort(LINEBREAK);
        }
    }
}